RSpec.describe Msf::Exploit::Remote::SMTPDeliver do

  context "#smtp_send_recv" do
    subject(:instance) {
      mod = Msf::Exploit::Remote.allocate
      mod.extend described_class
      mod
    }


    let (:socket) {
      double(Rex::Socket::Tcp)
    }
    let (:cmd) {
      "EHLO"
    }
    let(:ehlo_resp1) {
      "250-ip-10-140-50-23.us-west-1.compute.internal\r\n250-XXXA\r250-PIPELINING\r\n250-AUTH CRAM-MD5 LOGIN PLAIN\r\n"
    }
    let(:ehlo_resp2) {
      "250-SIZE 512000\r\n250-VRFY\r\n250-ETRN\r\n250-ENHANCEDSTATUSCODES\r\n250-8BITMIME\r\n250-XXXXXXXB\r\n250-XXXC\r\n"
    }
    let(:ehlo_resp3) {
      "250 DSN"
    }
    let(:ehlo_resp4) {
      "\r\n"
    }

    before {
      allow(instance).to receive(:vprint_status)
      allow(socket).to receive(:put)
      allow(socket).to receive(:get_once).and_return(ehlo_resp1, ehlo_resp2, ehlo_resp3, ehlo_resp4)
    }

    it "should read the socket for continuation messages" do
      response = instance.smtp_send_recv(cmd, socket)
      expect(response).to end_with(ehlo_resp3 + ehlo_resp4)
    end

    context "when a single response occurs" do
      let(:ehlo_resp1) {
        "250 DSN\r\n"
      }

      before {
        allow(socket).to receive(:get_once).and_return(ehlo_resp1)
      }


      it "passes" do
        response = instance.smtp_send_recv(cmd, socket)
        expect(response).to end_with(ehlo_resp1)
      end
    end

    context "when the server response is terse" do
      let(:ehlo_resp3) {
        "250"
      }

      it "should support a final line with no space or extra data" do
        response = instance.smtp_send_recv(cmd, socket)
        expect(response).to end_with(ehlo_resp3 + ehlo_resp4)
      end
    end

    context "when incomplete response is received" do
      # a nil from `get_once` simulates a Timeout expired
      let(:ehlo_resp4){
        nil
      }

      it "should raise error when the response is incomplete" do
        expect {instance.smtp_send_recv(cmd, socket)}.to raise_error Msf::Exploit::Remote::SMTPDeliver::SMTPCommunicationError
      end
    end

    context "when excess data response is received" do
      # a nil from `get_once` simulates a Timeout expired
      let(:ehlo_resp3){
        "250 DSN\r\n253 additional unexpected data"
      }
      let(:ehlo_resp4){
        nil
      }

      it "should raise error when the response is incomplete" do
        expect {instance.smtp_send_recv(cmd, socket)}.to raise_error Msf::Exploit::Remote::SMTPDeliver::SMTPCommunicationError
      end
    end
  end
end
